import mysql.connector
import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.multioutput import MultiOutputClassifier
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.model_selection import train_test_split, KFold
from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau
import tensorflow as tf
from datetime import datetime, timedelta
import json
from collections import Counter
import math
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, BatchNormalization, Dropout
from tensorflow.keras.optimizers import Adam
import scipy.stats as stats

class SSQPredictor:
    def __init__(self):
        self.mysql_config = {
            'host': 'localhost',
            'port': 3306,
            'user': 'root',
            'password': 'Duchenyi619',
            'database': 'redAndBlue'
        }
        
        # 双色球摇奖机物理参数（来自官方规格）
        self.machine_params = {
            'weight': 85000,            # 重量(g)
            'height': 2020,             # 高度(mm)
            'width': 750,               # 宽度(mm)
            'chamber_diameter': 700,     # 搅拌室直径(mm)
            'material': 'organic_glass', # 材质：高强度有机玻璃
            'manufacturer': 'Ryo-Catteau', # 厂商：法国Ryo-Catteau
            'model': 'Myosotis',         # 型号：Myosotis
            'origin': 'France',          # 产地：法国
            'rotation_speed': 20,        # 搅拌速度(rpm)
            'mixing_time': 25,           # 搅拌时间(s)
            'arm_count': 2,              # 搅拌臂数量
            'arm_rotation': 'reverse',   # 搅拌臂旋转方向：底部两个转盘逆向旋转
            'chamber_volume': 0.269,     # 搅拌室体积(m³)，根据直径和高度计算
            'material_density': 1200,    # 有机玻璃密度(kg/m³)
            'surface_roughness': 0.05    # 表面粗糙度(μm)
        }
        
        # 双色球摇奖球物理参数（来自官方规格）
        self.ball_params = {
            'weight': 25.0,             # 标准重量(g)
            'diameter': 50.0,           # 标准直径(mm)
            'weight_tolerance': 0.5,    # 重量误差(±g)
            'size_tolerance': 0.5,      # 直径误差(±mm)
            'material': 'rubber_foam',  # 材质：天然橡胶和闭孔泡沫
            'digit_count': 12,          # 球面数字个数
            'colors': ['red', 'blue'],  # 颜色：红色和蓝色
            'structure': 'solid',       # 内部结构：实心结构
            'red_count': 33,            # 红球数量
            'blue_count': 16,           # 蓝球数量
            'sets_count': 4,            # 球组数量：4套正式摇奖球
            'manufacturer': 'Ryo-Catteau', # 厂商
            'origin': 'France',         # 产地：法国
            'surface_area': 7853.98,    # 球表面积(mm²)
            'volume': 65449.85,         # 球体积(mm³)
            'density': 0.382,           # 密度(g/cm³)，根据重量和体积计算
            'elasticity': 0.75,         # 弹性系数
            'friction_coef': 0.35       # 摩擦系数
        }
        
        # 环境参数
        self.environment_params = {
            'temperature': 25,         # 室温(°C)
            'humidity': 45,            # 湿度(%)
            'air_pressure': 101.325,   # 气压(kPa)
            'air_resistance': 0.12,    # 空气阻力系数
            'gravity': 9.81,           # 重力加速度(m/s²)
            'air_density': 1.225,      # 空气密度(kg/m³)
            'air_viscosity': 1.81e-5   # 空气动力粘度(kg/m·s)
        }
        
        # 初始化模型
        self._init_models()
    
    def _init_models(self):
        """初始化红球和蓝球的预测模型"""
        # 红球随机森林模型 (33选6)
        self.red_rf = [RandomForestClassifier(
            n_estimators=200,
            max_depth=20,
            min_samples_split=5,
            min_samples_leaf=2,
            max_features='sqrt',
            random_state=42 + i,
            n_jobs=-1
        ) for i in range(6)]  # 6个独立的分类器
        
        # 蓝球随机森林模型 (16选1)
        self.blue_rf = RandomForestClassifier(
            n_estimators=200,
            max_depth=15,
            min_samples_split=4,
            min_samples_leaf=2,
            max_features='sqrt',
            random_state=42,
            n_jobs=-1
        )
        
        # 数据标准化
        self.red_scaler = StandardScaler()
        self.blue_scaler = StandardScaler()
        
        # 深度学习模型将在需要时构建
        self.red_dl_model = None
        self.blue_dl_model = None

    def build_red_dl_model(self, input_dim):
        """构建增强的红球深度学习模型"""
        model = Sequential([
            Dense(512, activation='relu', input_shape=(input_dim,)),
            BatchNormalization(),
            Dropout(0.4),
            
            Dense(256, activation='relu'),
            BatchNormalization(),
            Dropout(0.3),
            
            Dense(128, activation='relu'),
            BatchNormalization(),
            Dropout(0.2),
            
            Dense(64, activation='relu'),
            BatchNormalization(),
            
            Dense(33, activation='sigmoid')
        ])
        
        optimizer = Adam(learning_rate=0.001)
        
        model.compile(
            optimizer=optimizer,
            loss='binary_crossentropy',
            metrics=['accuracy', tf.keras.metrics.AUC()]
        )
        
        return model

    def build_blue_dl_model(self, input_dim):
        """构建增强的蓝球深度学习模型"""
        model = Sequential([
            Dense(256, activation='relu', input_shape=(input_dim,)),
            BatchNormalization(),
            Dropout(0.3),
            
            Dense(128, activation='relu'),
            BatchNormalization(),
            Dropout(0.2),
            
            Dense(64, activation='relu'),
            BatchNormalization(),
            
            Dense(16, activation='softmax')
        ])
        
        optimizer = Adam(learning_rate=0.0005)
        
        model.compile(
            optimizer=optimizer,
            loss='categorical_crossentropy',
            metrics=['accuracy', tf.keras.metrics.AUC()]
        )
        
        return model

    def _calculate_collision_dynamics(self, numbers, is_red=True):
        """计算碰撞动力学特征"""
        max_num = 33 if is_red else 16
        ball_mass = self.ball_params['weight'] / 1000  # kg
        ball_diameter = self.ball_params['diameter'] / 1000  # m
        chamber_radius = self.machine_params['chamber_diameter'] / 2000  # m
        chamber_height = self.machine_params['height'] / 1000  # m
        rotation_speed = self.machine_params['rotation_speed'] * 2 * np.pi / 60  # rad/s
        
        # 1. 球体运动约束
        max_velocity = rotation_speed * chamber_radius  # 最大切向速度
        min_velocity = max_velocity * 0.2  # 最小速度（考虑摩擦损耗）
        
        # 2. 碰撞参数
        restitution_coef = self.ball_params['elasticity']  # 恢复系数
        friction_coef = self.ball_params['friction_coef']  # 摩擦系数
        
        # 3. 碰撞频率计算
        # 平均自由程
        mean_free_path = self.machine_params['chamber_volume'] / (
            np.pi * ball_diameter**2 * max_num
        )
        
        # 考虑球的相对速度
        relative_velocity = max_velocity * np.sqrt(2)  # RMS速度
        collision_frequency = relative_velocity / mean_free_path
        
        # 4. 碰撞能量计算
        kinetic_energy = 0.5 * ball_mass * max_velocity**2
        energy_loss_per_collision = (1 - restitution_coef**2) * kinetic_energy
        
        # 5. 动量传递
        momentum_transfer = ball_mass * relative_velocity * (1 + restitution_coef)
        
        # 6. 球体间相互作用
        # 考虑范德华力
        hamaker_constant = 1e-19  # J (典型值)
        separation = ball_diameter * 0.1  # 假设最小分离距离
        van_der_waals = -hamaker_constant * ball_diameter / (12 * separation**2)
        
        # 7. 摩擦力矩
        normal_force = ball_mass * self.environment_params['gravity']
        friction_torque = friction_coef * normal_force * ball_diameter / 2
        
        # 8. 能量耗散率
        mixing_time = self.machine_params['mixing_time']
        total_collisions = collision_frequency * mixing_time
        total_energy_loss = energy_loss_per_collision * total_collisions
        energy_dissipation_rate = total_energy_loss / (kinetic_energy * mixing_time)
        
        # 9. 球体分布
        effective_volume = np.pi * chamber_radius**2 * chamber_height
        packing_density = (max_num * (4/3) * np.pi * (ball_diameter/2)**3) / effective_volume
        
        return {
            'collision_frequency': collision_frequency,
            'energy_loss_rate': energy_dissipation_rate,
            'momentum_transfer': momentum_transfer,
            'van_der_waals': van_der_waals,
            'friction_torque': friction_torque,
            'packing_density': packing_density
        }

    def _calculate_rotation_effects(self):
        """计算摇奖机转盘效应"""
        # 1. 转盘参数
        chamber_radius = self.machine_params['chamber_diameter'] / 2000  # m
        rotation_speed = self.machine_params['rotation_speed'] * 2 * np.pi / 60  # rad/s
        
        # 2. 科里奥利力
        ball_mass = self.ball_params['weight'] / 1000  # kg
        radial_velocity = rotation_speed * chamber_radius * 0.5  # 估计径向速度
        coriolis_force = 2 * ball_mass * rotation_speed * radial_velocity
        
        # 3. 离心力
        centrifugal_force = ball_mass * rotation_speed**2 * chamber_radius
        
        # 4. 转盘能量传递
        moment_of_inertia = ball_mass * (self.ball_params['diameter']/2000)**2
        rotational_energy = 0.5 * moment_of_inertia * rotation_speed**2
        
        # 5. 转盘效应系数
        rotation_effect = centrifugal_force / (ball_mass * self.environment_params['gravity'])
        
        return {
            'coriolis_force': coriolis_force,
            'centrifugal_force': centrifugal_force,
            'rotational_energy': rotational_energy,
            'rotation_effect': rotation_effect
        }

    def _calculate_environmental_effects(self):
        """计算环境因素影响"""
        # 1. 温度效应
        temp = self.environment_params['temperature']
        thermal_expansion = 7e-5  # 橡胶的线膨胀系数
        size_variation = self.ball_params['diameter'] * thermal_expansion * (temp - 20)
        
        # 2. 湿度效应
        humidity = self.environment_params['humidity']
        # 考虑材料吸水膨胀
        humidity_expansion = 1 + (humidity - 45) * 2e-5
        
        # 3. 气压效应
        pressure = self.environment_params['air_pressure']
        pressure_factor = pressure / 101.325
        
        # 4. 空气动力学效应
        air_density = self.environment_params['air_density'] * pressure_factor
        air_viscosity = self.environment_params['air_viscosity']
        
        # 5. 球体受力分析
        ball_diameter = self.ball_params['diameter'] / 1000
        cross_section = np.pi * (ball_diameter/2)**2
        
        # 6. 环境综合影响系数
        environmental_factor = (
            humidity_expansion * 
            pressure_factor * 
            (1 + (temp - 25) * 0.001)
        )
        
        return {
            'size_variation': size_variation,
            'humidity_effect': humidity_expansion,
            'pressure_effect': pressure_factor,
            'air_density': air_density,
            'environmental_factor': environmental_factor
        }

    def _calculate_ball_interactions(self, numbers, is_red=True):
        """计算球体之间的相互作用"""
        interactions = {}
        ball_mass = self.ball_params['weight'] / 1000  # kg
        ball_radius = self.ball_params['diameter'] / 2000  # m
        chamber_radius = self.machine_params['chamber_diameter'] / 2000  # m
        rotation_speed = self.machine_params['rotation_speed'] * 2 * np.pi / 60  # rad/s
        
        # 1. 球体间的静电力
        # 假设球体表面带有微弱电荷
        epsilon_0 = 8.85e-12  # 真空介电常数
        charge = 1e-12  # 假设的电荷量 (C)
        
        # 2. 球体间的范德华力参数
        hamaker_constant = 1e-19  # J
        min_separation = ball_radius * 0.1  # 最小分离距离
        
        # 3. 流体动力学参数
        fluid_density = self.environment_params['air_density']
        fluid_viscosity = self.environment_params['air_viscosity']
        
        # 4. 计算每对球之间的相互作用
        for i in range(len(numbers)):
            for j in range(i + 1, len(numbers)):
                # 基于号码差计算假设的空间距离
                number_diff = abs(numbers[i] - numbers[j])
                spatial_distance = (number_diff / (33 if is_red else 16)) * chamber_radius
                
                # 4.1 静电力
                electrostatic_force = (charge ** 2) / (4 * np.pi * epsilon_0 * spatial_distance ** 2)
                
                # 4.2 范德华力
                vdw_force = -hamaker_constant * ball_radius / (12 * spatial_distance ** 2)
                
                # 4.3 流体动力学相互作用
                # 使用Oseen张量计算流体介导的相互作用
                relative_velocity = rotation_speed * spatial_distance
                reynolds_number = fluid_density * relative_velocity * ball_radius / fluid_viscosity
                oseen_correction = 1 + 3/8 * reynolds_number  # Oseen修正
                
                # 4.4 碰撞概率
                collision_prob = np.exp(-spatial_distance / (4 * ball_radius))
                
                # 4.5 动量交换
                momentum_exchange = ball_mass * relative_velocity * collision_prob
                
                # 4.6 能量传递
                energy_transfer = 0.5 * ball_mass * relative_velocity ** 2 * collision_prob
                
                # 存储相互作用数据
                pair_key = f"{numbers[i]}-{numbers[j]}"
                interactions[pair_key] = {
                    'electrostatic': electrostatic_force,
                    'vdw': vdw_force,
                    'hydrodynamic': oseen_correction,
                    'collision_prob': collision_prob,
                    'momentum_exchange': momentum_exchange,
                    'energy_transfer': energy_transfer
                }
        
        # 5. 计算总体相互作用特征
        if interactions:
            avg_electrostatic = np.mean([v['electrostatic'] for v in interactions.values()])
            avg_vdw = np.mean([v['vdw'] for v in interactions.values()])
            avg_hydrodynamic = np.mean([v['hydrodynamic'] for v in interactions.values()])
            avg_collision_prob = np.mean([v['collision_prob'] for v in interactions.values()])
            avg_momentum = np.mean([v['momentum_exchange'] for v in interactions.values()])
            avg_energy = np.mean([v['energy_transfer'] for v in interactions.values()])
            
            # 归一化处理
            max_force = max(abs(avg_electrostatic), abs(avg_vdw))
            if max_force > 0:
                avg_electrostatic /= max_force
                avg_vdw /= max_force
            
            return [
                avg_electrostatic,
                avg_vdw,
                avg_hydrodynamic - 1,  # 归一化Oseen修正
                avg_collision_prob,
                avg_momentum / (ball_mass * rotation_speed * chamber_radius),  # 归一化动量
                avg_energy / (0.5 * ball_mass * (rotation_speed * chamber_radius) ** 2)  # 归一化能量
            ]
        
        return [0] * 6  # 如果没有相互作用返回零向量

    def _calculate_physical_constraints(self, numbers, is_red=True):
        """计算物理约束条件"""
        ball_mass = self.ball_params['weight'] / 1000  # kg
        ball_radius = self.ball_params['diameter'] / 2000  # m
        chamber_radius = self.machine_params['chamber_diameter'] / 2000  # m
        chamber_height = self.machine_params['height'] / 1000  # m
        rotation_speed = self.machine_params['rotation_speed'] * 2 * np.pi / 60  # rad/s
        
        # 1. 运动学约束
        max_velocity = rotation_speed * chamber_radius
        min_velocity = max_velocity * 0.1  # 考虑摩擦损耗后的最小速度
        
        # 2. 动力学约束
        max_force = ball_mass * (max_velocity ** 2) / chamber_radius  # 最大离心力
        max_torque = max_force * ball_radius  # 最大力矩
        
        # 3. 能量约束
        max_kinetic_energy = 0.5 * ball_mass * max_velocity ** 2
        max_potential_energy = ball_mass * self.environment_params['gravity'] * chamber_height
        
        # 4. 碰撞约束
        restitution_coef = self.ball_params['elasticity']
        max_impact_force = (1 + restitution_coef) * ball_mass * max_velocity / 0.001  # 假设碰撞时间为1ms
        
        # 5. 空间约束
        total_ball_volume = len(numbers) * (4/3) * np.pi * ball_radius**3
        chamber_volume = np.pi * chamber_radius**2 * chamber_height
        volume_ratio = total_ball_volume / chamber_volume
        
        # 6. 稳定性约束
        stability_threshold = 0.8  # 稳定性阈值
        current_stability = 1 - (volume_ratio / 0.74)  # 0.74是球的最大堆积密度
        
        # 7. 摩擦约束
        friction_coef = self.ball_params['friction_coef']
        max_friction_force = friction_coef * ball_mass * self.environment_params['gravity']
        
        return {
            'velocity_constraint': (min_velocity <= max_velocity),
            'force_constraint': max_force <= ball_mass * 100,  # 限制最大力为100g
            'energy_constraint': max_kinetic_energy <= max_potential_energy * 2,
            'impact_constraint': max_impact_force <= ball_mass * 1000,  # 限制冲击力
            'volume_constraint': volume_ratio <= 0.5,  # 确保足够的自由空间
            'stability_constraint': current_stability >= stability_threshold,
            'friction_constraint': max_friction_force >= ball_mass * max_velocity**2 / chamber_radius
        }

    def _calculate_enhanced_energy_loss(self, numbers, is_red=True):
        """计算增强的能量损耗"""
        ball_mass = self.ball_params['weight'] / 1000
        ball_radius = self.ball_params['diameter'] / 2000
        chamber_radius = self.machine_params['chamber_diameter'] / 2000
        rotation_speed = self.machine_params['rotation_speed'] * 2 * np.pi / 60
        
        # 1. 碰撞能量损耗
        restitution_coef = self.ball_params['elasticity']
        collision_energy_loss = (1 - restitution_coef**2)
        
        # 2. 摩擦能量损耗
        friction_coef = self.ball_params['friction_coef']
        normal_force = ball_mass * self.environment_params['gravity']
        friction_work = friction_coef * normal_force * (2 * np.pi * chamber_radius)
        
        # 3. 空气阻力损耗
        air_density = self.environment_params['air_density']
        drag_coef = self.environment_params['air_resistance']
        cross_section = np.pi * ball_radius**2
        air_resistance_loss = 0.5 * air_density * drag_coef * cross_section * (rotation_speed * chamber_radius)**2
        
        # 4. 涡流损耗
        fluid_viscosity = self.environment_params['air_viscosity']
        reynolds_number = air_density * rotation_speed * chamber_radius * ball_radius / fluid_viscosity
        eddy_loss = 0.1 * reynolds_number * fluid_viscosity * rotation_speed**2  # 简化的涡流损耗模型
        
        # 5. 变形能量损耗
        deformation_modulus = 1e6  # 假设的弹性模量
        max_deformation = ball_radius * 0.01  # 假设最大变形为半径的1%
        deformation_energy = 0.5 * deformation_modulus * max_deformation**2
        
        # 6. 声学能量损耗
        acoustic_efficiency = 0.01  # 假设1%的碰撞能量转化为声能
        acoustic_loss = acoustic_efficiency * collision_energy_loss
        
        # 7. 热能损耗
        thermal_conductivity = 0.15  # 橡胶的热导率
        temperature_diff = 5  # 假设温差5K
        thermal_loss = thermal_conductivity * 4 * np.pi * ball_radius**2 * temperature_diff
        
        total_energy_loss = (
            collision_energy_loss +
            friction_work +
            air_resistance_loss +
            eddy_loss +
            deformation_energy +
            acoustic_loss +
            thermal_loss
        )
        
        return {
            'collision_loss': collision_energy_loss,
            'friction_loss': friction_work,
            'air_resistance_loss': air_resistance_loss,
            'eddy_loss': eddy_loss,
            'deformation_loss': deformation_energy,
            'acoustic_loss': acoustic_loss,
            'thermal_loss': thermal_loss,
            'total_loss': total_energy_loss
        }

    def _calculate_temperature_effects(self, numbers, is_red=True):
        """计算温度对球体相互作用的影响"""
        temp = self.environment_params['temperature']
        base_temp = 20  # 基准温度
        temp_diff = temp - base_temp
        
        # 1. 材料属性变化
        thermal_expansion = 7e-5  # 橡胶的线膨胀系数
        youngs_modulus_temp_coef = -0.02  # 杨氏模量温度系数
        friction_temp_coef = -0.01  # 摩擦系数温度系数
        
        # 2. 尺寸变化
        size_change = 1 + thermal_expansion * temp_diff
        new_radius = (self.ball_params['diameter'] / 2000) * size_change
        
        # 3. 弹性变化
        base_elasticity = self.ball_params['elasticity']
        elasticity_change = 1 + youngs_modulus_temp_coef * temp_diff
        new_elasticity = base_elasticity * elasticity_change
        
        # 4. 摩擦变化
        base_friction = self.ball_params['friction_coef']
        friction_change = 1 + friction_temp_coef * temp_diff
        new_friction = base_friction * friction_change
        
        # 5. 空气粘度变化
        base_viscosity = self.environment_params['air_viscosity']
        sutherland_temp = 110.4  # 空气的Sutherland温度
        new_viscosity = base_viscosity * ((temp + 273.15)/(base_temp + 273.15))**1.5 * \
                       ((base_temp + 273.15 + sutherland_temp)/(temp + 273.15 + sutherland_temp))
        
        # 6. 能量传递效率
        energy_transfer_efficiency = 1 - 0.01 * abs(temp_diff)  # 每偏离1度降低1%效率
        
        # 7. 球体间作用力变化
        interaction_force_change = 1 - 0.005 * abs(temp_diff)  # 每偏离1度减少0.5%作用力
        
        return {
            'size_factor': size_change,
            'elasticity_factor': elasticity_change,
            'friction_factor': friction_change,
            'viscosity_factor': new_viscosity / base_viscosity,
            'energy_transfer_efficiency': energy_transfer_efficiency,
            'interaction_force_factor': interaction_force_change
        }

    def _calculate_enhanced_fluid_dynamics(self, numbers, is_red=True):
        """计算增强的流体动力学效应"""
        ball_radius = self.ball_params['diameter'] / 2000
        chamber_radius = self.machine_params['chamber_diameter'] / 2000
        rotation_speed = self.machine_params['rotation_speed'] * 2 * np.pi / 60
        
        # 1. 基本流体参数
        fluid_density = self.environment_params['air_density']
        fluid_viscosity = self.environment_params['air_viscosity']
        
        # 2. 无量纲参数
        reynolds_number = fluid_density * rotation_speed * chamber_radius * ball_radius / fluid_viscosity
        froude_number = rotation_speed**2 * chamber_radius / self.environment_params['gravity']
        strouhal_number = rotation_speed * ball_radius / (2 * np.pi * chamber_radius)
        
        # 3. 流场特征
        # 3.1 边界层厚度
        boundary_layer = 5 * np.sqrt(fluid_viscosity * chamber_radius / (fluid_density * rotation_speed))
        
        # 3.2 涡街特征
        karman_frequency = 0.2 * rotation_speed * chamber_radius / ball_radius
        
        # 3.3 尾流区域
        wake_length = ball_radius * (1 + 0.1 * reynolds_number**0.5)
        
        # 4. 流体力
        # 4.1 阻力系数
        if reynolds_number < 1:
            drag_coef = 24 / reynolds_number
        elif reynolds_number < 1000:
            drag_coef = 24 / reynolds_number * (1 + 0.15 * reynolds_number**0.687)
        else:
            drag_coef = 0.44
        
        # 4.2 升力系数
        lift_coef = 0.2 * (1 - np.exp(-reynolds_number / 1000))
        
        # 4.3 力的计算
        dynamic_pressure = 0.5 * fluid_density * (rotation_speed * chamber_radius)**2
        cross_section = np.pi * ball_radius**2
        
        drag_force = drag_coef * dynamic_pressure * cross_section
        lift_force = lift_coef * dynamic_pressure * cross_section
        
        # 5. 流体相互作用
        # 5.1 球体间流体耦合
        interaction_distance = 4 * ball_radius
        coupling_strength = np.exp(-len(numbers) / interaction_distance)
        
        # 5.2 流体诱导振动
        natural_frequency = np.sqrt(self.environment_params['gravity'] / ball_radius)
        vortex_shedding_ratio = karman_frequency / natural_frequency
        
        return {
            'reynolds_number': reynolds_number,
            'froude_number': froude_number,
            'strouhal_number': strouhal_number,
            'boundary_layer': boundary_layer,
            'wake_length': wake_length,
            'drag_force': drag_force,
            'lift_force': lift_force,
            'coupling_strength': coupling_strength,
            'vortex_shedding_ratio': vortex_shedding_ratio
        }

    def extract_physical_features(self, numbers, is_red=True):
        """根据双色球物理特性提取特征"""
        features = []
        sorted_nums = sorted(numbers)
        
        # 1. 基础物理特征
        ball_mass = self.ball_params['weight'] / 1000  # kg
        ball_volume = self.ball_params['volume'] / 1e9  # m³
        ball_density = ball_mass / ball_volume  # kg/m³
        
        # 2. 碰撞动力学
        collision_dynamics = self._calculate_collision_dynamics(numbers, is_red)
        
        # 3. 转盘效应
        rotation_effects = self._calculate_rotation_effects()
        
        # 4. 环境影响
        env_effects = self._calculate_environmental_effects()
        
        # 5. 球的运动学特征
        chamber_radius = self.machine_params['chamber_diameter'] / 2000
        rotation_speed = self.machine_params['rotation_speed'] * 2 * np.pi / 60
        
        # 6. 球体相互作用特征
        interaction_features = self._calculate_ball_interactions(numbers, is_red)
        
        # 7. 物理约束条件
        physical_constraints = self._calculate_physical_constraints(numbers, is_red)
        
        # 8. 能量损耗
        energy_loss = self._calculate_enhanced_energy_loss(numbers, is_red)
        
        # 9. 温度效应
        temperature_effects = self._calculate_temperature_effects(numbers, is_red)
        
        # 10. 流体动力学效应
        fluid_dynamics = self._calculate_enhanced_fluid_dynamics(numbers, is_red)
        
        # 合并所有物理特征
        physical_features = [
            ball_density/1000,                            # 密度
            rotation_speed * chamber_radius/10,           # 线速度
            collision_dynamics['collision_frequency']/100, # 碰撞频率
            collision_dynamics['energy_loss_rate'],       # 能量损失率
            collision_dynamics['momentum_transfer']/10,    # 动量传递
            collision_dynamics['friction_torque'],         # 摩擦力矩
            collision_dynamics['packing_density'],         # 填充密度
            rotation_effects['coriolis_force']/10,        # 科里奥利力
            rotation_effects['centrifugal_force']/10,     # 离心力
            rotation_effects['rotation_effect'],          # 转盘效应
            env_effects['environmental_factor'],          # 环境影响
            *interaction_features,                        # 球体相互作用特征
            float(physical_constraints['velocity_constraint']),  # 速度约束
            float(physical_constraints['stability_constraint']), # 稳定性约束
            energy_loss['total_loss']/1000,              # 总能量损失
            temperature_effects['energy_transfer_efficiency'], # 温度影响
            fluid_dynamics['coupling_strength'],          # 流体耦合强度
            fluid_dynamics['drag_force']/10,             # 阻力
            fluid_dynamics['lift_force']/10              # 升力
        ]
        
        # 归一化处理
        physical_features = np.array(physical_features)
        physical_features = np.clip(physical_features, -1, 1)
        
        features.extend(physical_features)
        return features

    def load_data(self):
        """从MySQL加载双色球历史数据"""
        conn = mysql.connector.connect(**self.mysql_config)
        query = """
            SELECT 
                draw_number as period,
                draw_date,
                CONCAT('[', red_ball_1, ',', red_ball_2, ',', red_ball_3, ',', 
                       red_ball_4, ',', red_ball_5, ',', red_ball_6, ']') as red_numbers,
                blue_ball as blue_number
            FROM lottery_data
            ORDER BY draw_number ASC
        """
        df = pd.read_sql(query, conn)
        conn.close()
        
        # 转换红球字符串为列表
        df['red_numbers'] = df['red_numbers'].apply(lambda x: [int(n) for n in x.strip('[]').split(',')])
        df['blue_number'] = df['blue_number'].astype(int)
        
        return df

    def preprocess_data(self, df):
        """增强的数据预处理"""
        print("开始数据预处理...")
        
        if len(df) < 10:  # 确保有足够的历史数据
            return np.array([]), np.array([]), np.array([]), np.array([])
        
        # 获取热号冷号分析
        red_freq, blue_freq = self.analyze_hot_cold_numbers(df)
        
        # 获取遗漏值分析
        red_missing, blue_missing = self.calculate_missing_values(df)
        
        # 特征和标签列表
        red_features = []
        blue_features = []
        red_labels = []
        blue_labels = []
        
        # 设置序列长度
        sequence_length = 10
        
        for i in range(len(df) - sequence_length):
            # 准备序列数据
            sequence = df.iloc[i:i+sequence_length]
            next_draw = df.iloc[i+sequence_length]
            
            # 红球特征
            red_sequence_features = []
            for _, row in sequence.iterrows():
                # 基础特征
                red_basic = self.extract_basic_features(row['red_numbers'])
                # 统计特征
                red_stats = self.extract_statistical_features(row['red_numbers'])
                # 物理特征
                red_phys = self.extract_physical_features(row['red_numbers'], is_red=True)
                # 热号冷号特征
                red_hot_cold = [red_freq.get(n, 0)/100 for n in range(1, 34)]
                # 遗漏值特征
                red_miss = [red_missing.get(n, 0)/100 for n in range(1, 34)]
                # 合并特征
                red_sequence_features.extend(red_basic + red_stats + red_phys + red_hot_cold + red_miss)
            
            # 蓝球特征
            blue_sequence_features = []
            for _, row in sequence.iterrows():
                # 基础特征
                blue_basic = self.extract_basic_features([row['blue_number']])
                # 统计特征
                blue_stats = self.extract_statistical_features([row['blue_number']])
                # 物理特征
                blue_phys = self.extract_physical_features([row['blue_number']], is_red=False)
                # 热号冷号特征
                blue_hot_cold = [blue_freq.get(n, 0)/100 for n in range(1, 17)]
                # 遗漏值特征
                blue_miss = [blue_missing.get(n, 0)/100 for n in range(1, 17)]
                # 合并特征
                blue_sequence_features.extend(blue_basic + blue_stats + blue_phys + blue_hot_cold + blue_miss)
            
            # 添加到特征列表
            red_features.append(red_sequence_features)
            blue_features.append(blue_sequence_features)
            
            # 准备标签
            red_label = np.zeros(33)
            red_label[[n-1 for n in next_draw['red_numbers']]] = 1
            red_labels.append(red_label)
            
            blue_label = np.zeros(16)
            blue_label[next_draw['blue_number']-1] = 1
            blue_labels.append(blue_label)
        
        return np.array(red_features), np.array(blue_features), np.array(red_labels), np.array(blue_labels)

    def extract_basic_features(self, numbers):
        """提取基础特征"""
        features = []
        
        # 奇偶比
        odd_count = sum(1 for x in numbers if x % 2 == 1)
        even_count = len(numbers) - odd_count
        odd_even_ratio = odd_count / len(numbers)
        
        # 大小比（以最大值一半为界）
        max_num = 33 if len(numbers) > 1 else 16
        big_count = sum(1 for x in numbers if x > max_num/2)
        small_count = len(numbers) - big_count
        big_small_ratio = big_count / len(numbers)
        
        # 质数比
        def is_prime(n):
            if n < 2:
                return False
            for i in range(2, int(math.sqrt(n)) + 1):
                if n % i == 0:
                    return False
            return True
        
        prime_count = sum(1 for x in numbers if is_prime(x))
        prime_ratio = prime_count / len(numbers)
        
        features.extend([
            odd_even_ratio,
            big_small_ratio,
            prime_ratio
        ])
        
        return features

    def extract_statistical_features(self, numbers):
        """提取统计特征"""
        if not numbers:
            return [0] * 15  # 返回适当长度的零向量
            
        features = []
        sorted_nums = sorted(numbers)
        
        # 基本统计量
        mean = np.mean(numbers)
        std = np.std(numbers)
        median = np.median(numbers)
        
        # 间隔特征
        if len(numbers) > 1:
            gaps = np.diff(sorted_nums)
            mean_gap = np.mean(gaps)
            std_gap = np.std(gaps)
            max_gap = np.max(gaps)
            min_gap = np.min(gaps)
        else:
            mean_gap = std_gap = max_gap = min_gap = 0
        
        # 和值特征
        sum_value = sum(numbers)
        
        # AC值（邻号间隔）
        ac_value = sum(1 for i in range(len(sorted_nums)-1) if sorted_nums[i+1] - sorted_nums[i] == 1)
        
        # 尾数特征
        tail_numbers = [n % 10 for n in numbers]
        tail_mean = np.mean(tail_numbers)
        tail_std = np.std(tail_numbers)
        
        # 区间特征
        max_num = 33 if len(numbers) > 1 else 16
        zones = [0] * 3
        zone_size = max_num // 3
        for n in numbers:
            zone_idx = (n - 1) // zone_size
            if zone_idx >= 3:
                zone_idx = 2
            zones[zone_idx] += 1
        
        features.extend([
            mean/33 if len(numbers) > 1 else mean/16,
            std/33 if len(numbers) > 1 else std/16,
            median/33 if len(numbers) > 1 else median/16,
            mean_gap/33 if len(numbers) > 1 else 0,
            std_gap/33 if len(numbers) > 1 else 0,
            max_gap/33 if len(numbers) > 1 else 0,
            min_gap/33 if len(numbers) > 1 else 0,
            sum_value/200 if len(numbers) > 1 else sum_value/16,
            ac_value/5 if len(numbers) > 1 else 0,
            tail_mean/10,
            tail_std/10,
            *[z/len(numbers) for z in zones]
        ])
        
        return features

    def analyze_hot_cold_numbers(self, df, window=30):
        """分析热号和冷号"""
        recent_draws = df.tail(window)
        
        # 红球分析
        red_freq = {}
        for i in range(1, 34):
            count = sum(1 for nums in recent_draws['red_numbers'] if i in nums)
            red_freq[i] = count/window
        
        # 蓝球分析
        blue_freq = {}
        for i in range(1, 17):
            count = sum(1 for num in recent_draws['blue_number'] if num == i)
            blue_freq[i] = count/window
            
        return red_freq, blue_freq

    def calculate_missing_values(self, df):
        """计算遗漏值"""
        # 红球遗漏值
        red_missing = {}
        for i in range(1, 34):
            missing = 0
            for _, row in df.iloc[::-1].iterrows():
                if i in row['red_numbers']:
                    break
                missing += 1
            red_missing[i] = missing
        
        # 蓝球遗漏值
        blue_missing = {}
        for i in range(1, 17):
            missing = 0
            for _, row in df.iloc[::-1].iterrows():
                if row['blue_number'] == i:
                    break
                missing += 1
            blue_missing[i] = missing
        
        return red_missing, blue_missing

    def train_models(self):
        """训练增强的预测模型"""
        print("开始加载数据...")
        df = self.load_data()
        print(f"共加载 {len(df)} 条历史记录\n")
        
        # 数据预处理
        red_features, blue_features, red_labels, blue_labels = self.preprocess_data(df)
        
        if len(red_features) == 0:
            print("数据量不足，无法训练模型")
            return {'red_rf_score': 0, 'blue_rf_score': 0}
        
        # 数据标准化
        self.red_scaler = StandardScaler()
        self.blue_scaler = StandardScaler()
        
        red_features_scaled = self.red_scaler.fit_transform(red_features)
        blue_features_scaled = self.blue_scaler.fit_transform(blue_features)
        
        # 划分训练集和测试集
        red_train_features, red_test_features, red_train_labels, red_test_labels = train_test_split(
            red_features_scaled, red_labels, test_size=0.2, random_state=42
        )
        
        blue_train_features, blue_test_features, blue_train_labels, blue_test_labels = train_test_split(
            blue_features_scaled, blue_labels, test_size=0.2, random_state=42
        )
        
        # 训练红球模型
        print("\n训练红球模型...")
        red_rf_scores = []
        for i, rf in enumerate(self.red_rf):
            # 为每个分类器准备标签
            red_labels_i = red_train_labels[:, i]
            rf.fit(red_train_features, red_labels_i)
            score = rf.score(red_test_features, red_test_labels[:, i])
            red_rf_scores.append(score)
        
        print("训练红球深度学习模型...")
        if self.red_dl_model is None:
            self.red_dl_model = self.build_red_dl_model(red_train_features.shape[1])
        
        early_stopping = EarlyStopping(
            monitor='val_loss',
            patience=5,
            restore_best_weights=True
        )
        
        reduce_lr = ReduceLROnPlateau(
            monitor='val_loss',
            factor=0.5,
            patience=3,
            min_lr=0.0001
        )
        
        self.red_dl_model.fit(
            red_train_features,
            red_train_labels,
            epochs=50,
            batch_size=32,
            validation_split=0.2,
            callbacks=[early_stopping, reduce_lr],
            verbose=1
        )
        
        # 训练蓝球模型
        print("\n训练蓝球模型...")
        # 将蓝球one-hot标签转换为类别标签
        blue_train_labels_cat = np.argmax(blue_train_labels, axis=1)
        blue_test_labels_cat = np.argmax(blue_test_labels, axis=1)
        
        self.blue_rf.fit(blue_train_features, blue_train_labels_cat)
        blue_rf_score = self.blue_rf.score(blue_test_features, blue_test_labels_cat)        
        print("训练蓝球深度学习模型...")
        if self.blue_dl_model is None:
            self.blue_dl_model = self.build_blue_dl_model(blue_train_features.shape[1])
        
        self.blue_dl_model.fit(
            blue_train_features,
            blue_train_labels,
            epochs=50,
            batch_size=32,
            validation_split=0.2,
            callbacks=[early_stopping, reduce_lr],
            verbose=1
        )
        
        return {
            'red_rf_score': np.mean(red_rf_scores),
            'blue_rf_score': blue_rf_score
        }

    def predict_next(self, num_predictions=5, candidate_pool_size=20):
        """预测下一期号码
        
        Args:
            num_predictions: 最终返回的预测组数
            candidate_pool_size: 候选池大小，从多少组候选中挑选最优的
        """
        try:
            # 加载最近的历史数据
            df = self.load_data()
            if len(df) < 10:
                raise ValueError("历史数据不足")
            
            # 获取最近10期数据
            recent_data = df.tail(10)
            
            # 提取特征
            red_features = []
            blue_features = []
            
            for _, row in recent_data.iterrows():
                # 红球特征
                red_basic = self.extract_basic_features(row['red_numbers'])
                red_stats = self.extract_statistical_features(row['red_numbers'])
                red_phys = self.extract_physical_features(row['red_numbers'], is_red=True)
                red_hot_cold = [self.analyze_hot_cold_numbers(df)[0].get(n, 0) for n in range(1, 34)]
                red_miss = [self.calculate_missing_values(df)[0].get(n, 0) for n in range(1, 34)]
                red_features.extend(red_basic + red_stats + red_phys + red_hot_cold + red_miss)
                
                # 蓝球特征
                blue_basic = self.extract_basic_features([row['blue_number']])
                blue_stats = self.extract_statistical_features([row['blue_number']])
                blue_phys = self.extract_physical_features([row['blue_number']], is_red=False)
                blue_hot_cold = [self.analyze_hot_cold_numbers(df)[1].get(n, 0) for n in range(1, 17)]
                blue_miss = [self.calculate_missing_values(df)[1].get(n, 0) for n in range(1, 17)]
                blue_features.extend(blue_basic + blue_stats + blue_phys + blue_hot_cold + blue_miss)
            
            # 转换为numpy数组并重塑
            red_features = np.array(red_features).reshape(1, -1)
            blue_features = np.array(blue_features).reshape(1, -1)
            
            # 标准化特征
            red_features_scaled = self.red_scaler.transform(red_features)
            blue_features_scaled = self.blue_scaler.transform(blue_features)
            
            # 生成候选池
            print(f"🎯 生成 {candidate_pool_size} 组候选预测...")
            candidate_predictions = []
            for _ in range(candidate_pool_size):
                try:
                    # 预测红球
                    red_probs = np.zeros((6, 33))
                    for i, rf in enumerate(self.red_rf):
                        probs = rf.predict_proba(red_features_scaled)
                        red_probs[i] = probs[:, 1] if probs.shape[1] == 2 else probs[0]
                    
                    # 获取深度学习模型的预测并确保是numpy数组
                    red_dl_pred = self.red_dl_model.predict(red_features_scaled)
                    print(f"红球深度学习预测结果类型: {type(red_dl_pred)}")
                    print(f"红球深度学习预测结果形状: {np.array(red_dl_pred).shape if hasattr(red_dl_pred, 'shape') else len(red_dl_pred)}")
                    
                    # 转换预测结果为numpy数组
                    if isinstance(red_dl_pred, list):
                        red_dl_pred = np.array(red_dl_pred[0] if len(red_dl_pred) > 0 else np.zeros(33))
                    elif isinstance(red_dl_pred, np.ndarray):
                        if len(red_dl_pred.shape) == 1:
                            red_dl_pred = red_dl_pred.reshape(1, -1)
                        elif len(red_dl_pred.shape) > 2:
                            red_dl_pred = red_dl_pred.reshape(1, -1)
                    
                    # 组合预测结果
                    red_combined = np.zeros(33)
                    for i in range(33):
                        red_combined[i] = (np.mean(red_probs[:, i]) + red_dl_pred[0][i]) / 2
                    
                    # 添加轻微随机扰动以增加多样性（减少扰动强度）
                    red_combined += np.random.normal(0, 0.02, red_combined.shape)
                    
                    # 确保至少选择6个不同的号码
                    red_indices = []
                    prob_order = np.argsort(red_combined)[::-1]
                    for idx in prob_order:
                        if len(red_indices) < 6 and idx not in red_indices:
                            red_indices.append(idx)
                    
                    red_numbers = sorted([int(i + 1) for i in red_indices])
                    red_confidence = float(np.mean([red_combined[i] for i in red_indices]))
                    
                    # 预测蓝球
                    blue_rf_probs = self.blue_rf.predict_proba(blue_features_scaled)
                    print(f"蓝球随机森林预测结果类型: {type(blue_rf_probs)}")
                    print(f"蓝球随机森林预测结果形状: {blue_rf_probs.shape}")
                    
                    # 获取深度学习模型的预测并确保是numpy数组
                    blue_dl_pred = self.blue_dl_model.predict(blue_features_scaled)
                    print(f"蓝球深度学习预测结果类型: {type(blue_dl_pred)}")
                    print(f"蓝球深度学习预测结果形状: {np.array(blue_dl_pred).shape if hasattr(blue_dl_pred, 'shape') else len(blue_dl_pred)}")
                    
                    # 转换预测结果为numpy数组
                    if isinstance(blue_dl_pred, list):
                        blue_dl_pred = np.array(blue_dl_pred[0] if len(blue_dl_pred) > 0 else np.zeros(16))
                    elif isinstance(blue_dl_pred, np.ndarray):
                        if len(blue_dl_pred.shape) == 1:
                            blue_dl_pred = blue_dl_pred.reshape(1, -1)
                        elif len(blue_dl_pred.shape) > 2:
                            blue_dl_pred = blue_dl_pred.reshape(1, -1)
                    
                    # 修复蓝球预测逻辑 - 正确处理多分类结果
                    if blue_rf_probs.shape[1] == 16:  # 多分类情况 (16个蓝球号码)
                        blue_rf_combined = blue_rf_probs[0]  # 直接使用概率分布
                    else:  # 其他情况，使用随机分布
                        blue_rf_combined = np.random.dirichlet(np.ones(16))
                    
                    # 确保深度学习预测结果正确索引
                    if blue_dl_pred.shape == (1, 16):
                        blue_dl_combined = blue_dl_pred[0]  # 取第一行
                    else:
                        blue_dl_combined = blue_dl_pred.flatten()[:16]  # 展平并取前16个
                    
                    # 组合预测结果
                    blue_combined = np.zeros(16)
                    for i in range(16):
                        blue_combined[i] = (blue_rf_combined[i] + blue_dl_combined[i]) / 2
                    
                    # 添加轻微随机扰动确保多样性（减少扰动强度）
                    blue_combined += np.random.normal(0, 0.02, blue_combined.shape)
                    blue_combined = np.maximum(blue_combined, 0)  # 确保非负
                    
                    # 归一化概率分布
                    blue_combined = blue_combined / np.sum(blue_combined)
                    
                    # 使用加权随机选择而不是argmax
                    blue_probs = blue_combined / np.sum(blue_combined)
                    blue_index = np.random.choice(16, p=blue_probs)
                    blue_number = int(blue_index + 1)
                    blue_confidence = float(blue_combined[blue_index])
                    
                    # 计算质量分数
                    quality_score = float(self._calculate_prediction_quality(
                        red_numbers, 
                        blue_number,
                        red_confidence,
                        blue_confidence
                    ))
                    
                    candidate_predictions.append({
                        'red_numbers': red_numbers,
                        'blue_number': blue_number,
                        'quality_score': quality_score,
                        'confidence': {
                            'red': red_confidence,
                            'blue': blue_confidence
                        }
                    })
                except Exception as e:
                    print(f"单次预测出错: {str(e)}")
                    continue
            
            # 按质量分数排序候选池
            candidate_predictions.sort(key=lambda x: x['quality_score'], reverse=True)
            
            # 选择前num_predictions组作为最终结果
            final_predictions = candidate_predictions[:num_predictions]
            
            # 为最终结果添加排名
            for i, pred in enumerate(final_predictions):
                pred['rank'] = i + 1
            
            print(f"✅ 从 {len(candidate_predictions)} 组候选中选出得分最高的 {len(final_predictions)} 组")
            
            return final_predictions
            
        except Exception as e:
            print(f"预测过程出错: {str(e)}")
            return []

    def _calculate_prediction_quality(self, red_numbers, blue_number, red_confidence, blue_confidence):
        """计算增强的预测质量分数"""
        score = 0
        
        # 1. 号码分布评分 (25%)
        red_distribution = self._evaluate_distribution(red_numbers)
        score += red_distribution * 0.25
        
        # 2. 历史频率评分 (25%)
        red_freq_score = red_confidence
        blue_freq_score = blue_confidence
        score += (red_freq_score * 0.2 + blue_freq_score * 0.05)
        
        # 3. 号码组合合理性评分 (20%)
        combination_score = self._evaluate_combination_reasonableness(red_numbers, blue_number)
        score += combination_score * 0.2
        
        # 4. 物理特征一致性评分 (15%)
        physical_score = self._evaluate_physical_consistency(red_numbers, blue_number)
        score += physical_score * 0.15
        
        # 5. 统计特征评分 (15%)
        statistical_score = self._evaluate_statistical_features(red_numbers, blue_number)
        score += statistical_score * 0.15
        
        return score

    def _evaluate_distribution(self, numbers):
        """评估号码分布"""
        score = 0
        
        # 1. 奇偶比例
        odd_count = sum(1 for n in numbers if n % 2 == 1)
        even_count = len(numbers) - odd_count
        score += (1 - abs(odd_count - even_count) / len(numbers)) * 0.4
        
        # 2. 区间分布
        zones = [0] * 3
        for n in numbers:
            zones[(n-1)//11] += 1
        zone_balance = 1 - np.std(zones) / np.mean(zones) if np.mean(zones) > 0 else 0
        score += zone_balance * 0.6
        
        return score

    def _evaluate_combination_reasonableness(self, red_numbers, blue_number):
        """评估号码组合的合理性"""
        score = 0
        
        # 1. 红球和值合理性 (1-33选6，理想和值约102)
        red_sum = sum(red_numbers)
        ideal_sum = 102
        sum_score = 1 - abs(red_sum - ideal_sum) / ideal_sum
        score += max(0, sum_score) * 0.3
        
        # 2. 红球跨度合理性 (理想跨度约25-30)
        red_span = max(red_numbers) - min(red_numbers)
        ideal_span = 27
        span_score = 1 - abs(red_span - ideal_span) / ideal_span
        score += max(0, span_score) * 0.3
        
        # 3. 连号合理性 (避免过多连号)
        consecutive_count = 0
        sorted_red = sorted(red_numbers)
        for i in range(len(sorted_red) - 1):
            if sorted_red[i+1] - sorted_red[i] == 1:
                consecutive_count += 1
        consecutive_score = max(0, 1 - consecutive_count / 3)  # 最多允许2个连号
        score += consecutive_score * 0.2
        
        # 4. 红蓝球关联性
        blue_zone = (blue_number - 1) // 5 + 1  # 将蓝球分为3个区间
        red_zones = [(n - 1) // 11 + 1 for n in red_numbers]
        zone_diversity = len(set(red_zones)) / 3
        score += zone_diversity * 0.2
        
        return score

    def _evaluate_physical_consistency(self, red_numbers, blue_number):
        """评估物理特征一致性"""
        score = 0
        
        # 1. 基于物理特征的号码选择合理性
        # 考虑摇奖机的物理特性，某些号码组合在物理上更合理
        
        # 2. 球体重量分布
        # 假设不同号码的球体重量略有差异
        weight_variance = np.var(red_numbers)
        ideal_variance = 91.25  # 1-33的方差
        weight_score = 1 - abs(weight_variance - ideal_variance) / ideal_variance
        score += max(0, weight_score) * 0.4
        
        # 3. 碰撞概率分布
        # 基于号码间隔的碰撞概率
        sorted_red = sorted(red_numbers)
        gaps = [sorted_red[i+1] - sorted_red[i] for i in range(len(sorted_red)-1)]
        gap_variance = np.var(gaps) if len(gaps) > 0 else 0
        ideal_gap_variance = 20  # 理想间隔方差
        gap_score = 1 - abs(gap_variance - ideal_gap_variance) / ideal_gap_variance
        score += max(0, gap_score) * 0.3
        
        # 4. 蓝球与红球的物理关联
        blue_red_correlation = 1 - abs(blue_number - np.mean(red_numbers)) / 33
        score += max(0, blue_red_correlation) * 0.3
        
        return score

    def _evaluate_statistical_features(self, red_numbers, blue_number):
        """评估统计特征"""
        score = 0
        
        # 1. AC值 (邻号间隔数)
        sorted_red = sorted(red_numbers)
        ac_value = sum(1 for i in range(len(sorted_red)-1) if sorted_red[i+1] - sorted_red[i] == 1)
        ideal_ac = 2  # 理想的AC值
        ac_score = 1 - abs(ac_value - ideal_ac) / ideal_ac
        score += max(0, ac_score) * 0.3
        
        # 2. 质数比例
        def is_prime(n):
            if n < 2:
                return False
            for i in range(2, int(np.sqrt(n)) + 1):
                if n % i == 0:
                    return False
            return True
        
        prime_count = sum(1 for n in red_numbers if is_prime(n))
        prime_ratio = prime_count / len(red_numbers)
        ideal_prime_ratio = 0.4  # 理想质数比例
        prime_score = 1 - abs(prime_ratio - ideal_prime_ratio) / ideal_prime_ratio
        score += max(0, prime_score) * 0.3
        
        # 3. 尾数分布
        tail_numbers = [n % 10 for n in red_numbers]
        tail_diversity = len(set(tail_numbers)) / len(tail_numbers)
        score += tail_diversity * 0.2
        
        # 4. 蓝球统计特征
        blue_is_prime = 1 if is_prime(blue_number) else 0
        blue_odd_even = blue_number % 2
        red_odd_ratio = sum(1 for n in red_numbers if n % 2 == 1) / len(red_numbers)
        blue_consistency = 1 - abs(blue_odd_even - red_odd_ratio)
        score += blue_consistency * 0.2
        
        return score

    def save_latest_result(self, period, red_numbers, blue_number, draw_date=None):
        """保存最新一期开奖结果"""
        if draw_date is None:
            draw_date = datetime.now().strftime('%Y-%m-%d')
            
        try:
            conn = mysql.connector.connect(**self.mysql_config)
            cursor = conn.cursor()
            
            # 检查期号是否已存在
            check_query = "SELECT draw_number FROM lottery_data WHERE draw_number = %s"
            cursor.execute(check_query, (period,))
            if cursor.fetchone():
                print(f"期号 {period} 已存在，跳过插入")
                return False
            
            # 插入新记录
            insert_query = """
                INSERT INTO lottery_data (
                    draw_number, draw_date, 
                    red_ball_1, red_ball_2, red_ball_3, 
                    red_ball_4, red_ball_5, red_ball_6, 
                    blue_ball
                )
                VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
            """
            
            # 确保红球是排序的
            sorted_red = sorted(red_numbers)
            
            cursor.execute(insert_query, (
                period,
                draw_date,
                sorted_red[0],
                sorted_red[1],
                sorted_red[2],
                sorted_red[3],
                sorted_red[4],
                sorted_red[5],
                blue_number
            ))
            
            conn.commit()
            print(f"成功保存第 {period} 期开奖结果")
            return True
            
        except mysql.connector.Error as err:
            print(f"数据库错误: {err}")
            return False
            
        finally:
            if 'conn' in locals() and conn.is_connected():
                cursor.close()
                conn.close()

    def enhance_features(self, df):
        """增强特征工程"""
        # 1. 添加趋势特征
        df['trend_features'] = df['red_numbers'].apply(lambda x: self._calculate_trend_features(x))
        
        # 2. 添加组合特征
        df['combination_features'] = df['red_numbers'].apply(lambda x: self._calculate_combination_features(x))
        
        # 3. 添加周期特征
        df['cycle_features'] = df.apply(lambda row: self._calculate_cycle_features(row), axis=1)
        
        return df

def main():
    predictor = SSQPredictor()
    print("双色球预测模型初始化完成")
    
    print("\n=== 开始模型训练 ===")
    scores = predictor.train_models()
    
    print("\n=== 模型评分 ===")
    print(f"红球随机森林得分: {scores['red_rf_score']:.4f}")
    print(f"蓝球随机森林得分: {scores['blue_rf_score']:.4f}")
    
    print("\n=== 预测下一期号码 ===")
    predictions = predictor.predict_next(num_predictions=5)
    
    print("\n=== 按质量分数排序的预测结果 ===")
    for i, pred in enumerate(predictions, 1):
        print(f"\n第{i}组预测:")
        print(f"红球: {pred['red_numbers']}")
        print(f"蓝球: {pred['blue_number']}")
        print(f"质量分数: {pred['quality_score']:.4f}")
        print(f"红球置信度: {pred['confidence']['red']:.4f}")
        print(f"蓝球置信度: {pred['confidence']['blue']:.4f}")
    
    # 保存预测结果
    with open('ssq_predictions.json', 'w') as f:
        json.dump({
            'prediction_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
            'predictions': predictions,
            'model_scores': {
                'red_rf_score': float(scores['red_rf_score']),
                'blue_rf_score': float(scores['blue_rf_score'])
            }
        }, f, indent=2, cls=json.JSONEncoder)
        print("\n预测结果已保存到 ssq_predictions.json")

if __name__ == "__main__":
    main() 